﻿using Basic.Cluster.Abstract;
using Basic.Core.Application.Services;
using Basic.RPC;
using Basic.RPC.Abstract;
using Basic.RPC.Impl;
using BeetleX.XRPC.Extensions;
using BeetleX.XRPC.Packets;
using Hprose.RPC;
using Hprose.RPC.Plugins.Log;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Net;
using System.Reflection;
using static System.CoreExtension;

namespace System;

public static class BasicRPCExtension
{
    #region 注册为rpc服务端

    private static ConcurrentDictionary<Type, Type> Dict = new ConcurrentDictionary<Type, Type>();

    /// <summary>
    /// 注册RPC服务端
    /// </summary>
    /// <param name="services"></param>
    /// <param name="section"></param>
    /// <param name="assemblies"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static IServiceCollection AddBasicRpcService(this IServiceCollection services, IConfigurationSection section)
    {
        Assembly[] assemblies = GetApplicationServiceAssembly();
        if (assemblies.Length == 0)
            return services;

        var config = section.Get<BasicRpcServerConfig>();

        Type baseInterface = typeof(IApplicationService);
        List<Type> serviceBaseTypes = new List<Type>(128);
        List<Type> serviceImplTypes = new List<Type>(128);

        foreach (Assembly assembly in assemblies)
        {
            var publicTypes = assembly.GetExportedTypes();

            var interfaceTypes = publicTypes.Where(s => s.IsInterface && s.GetInterface(baseInterface.Name) != null).ToList();
            serviceBaseTypes.AddRange(interfaceTypes);

            var subTypes = publicTypes.Where(s => !s.IsInterface && !s.IsAbstract && s.GetInterface(baseInterface.Name) != null).ToList();
            serviceImplTypes.AddRange(subTypes);
        }

        foreach (Type baseType in serviceBaseTypes)
        {
            var implType = serviceImplTypes.Where(s => s.GetInterfaces().Contains(baseType)).FirstOrDefault();
            if (implType != null)
                Dict.TryAdd(baseType, implType);
        }

        HttpListener httpListener = new HttpListener();
        httpListener.Prefixes.Add($"http://127.0.0.1:{config.RpcPort}/");
        httpListener.Start();

        var log = new Log();
        Service rpcService = new Service();
        rpcService.Use(log.InvokeHandler).Use(log.IOHandler);

        //InvokeHandler myInvokeHandler = (name, arg, context, next) =>
        //{
        //    handler?.Invoke(context as ServiceContext);
        //    return next(name, arg, context);
        //};
        //rpcService.Use(myInvokeHandler);
        rpcService?.Bind(httpListener);

        services.AddSingleton(httpListener);
        services.AddSingleton(rpcService);
        return services;
    }

    /// <summary>
    /// 启用RPC服务端
    /// </summary>
    /// <param name="app"></param>
    public static void UseBasicRpcService(this IApplicationBuilder app)
    {
        var service = app.ApplicationServices.GetService<Service>();
        if (service != null)
            foreach (var kv in Dict)
            {
                var obj = app.ApplicationServices.GetService(kv.Key);
                service.AddInstanceMethods(obj, kv.Key.Name);
            }
    }

    #endregion

    #region HproseRpcClient

    /// <summary>
    /// 添加RPC客户端
    /// </summary>
    /// <param name="services"></param>
    /// <param name="section"></param>
    /// <returns></returns>
    public static IServiceCollection AddBasicRpcClient(this IServiceCollection services, IConfigurationSection section)
    {
        services.Configure<List<RpcClientService>>(section);
        services.AddSingleton<IRpcClient>(p =>
        {
            var options = p.GetService<IOptions<List<RpcClientService>>>();

            var discover = p.GetService<IDiscover>();
            if (discover == null)
                throw new ArgumentNullException("IDiscover Not added to IServiceCollection");

            var clusterService = discover.GetService();
            IDictionary<string, IDictionary<string, string>> serviceDict = new Dictionary<string, IDictionary<string, string>>();
            foreach (var kv in clusterService)
                serviceDict.TryAdd(kv.Key, kv.Value.Meta);

            var client = new HproseRpcClient(options.Value, serviceDict);
            client.Initialize();
            return client;
        });
        return services;
    }

    #endregion


    #region XRpc

    public static IServiceCollection AddXRpcServer(this IServiceCollection services, IConfigurationSection section, Assembly[] assemblies = null)
    {
        var config = section.Get<BasicRpcServerConfig>();
        services.UseXRPC(server =>
        {
            server.ServerOptions.LogLevel = BeetleX.EventArgs.LogType.Error;
            server.ServerOptions.DefaultListen.Port = config.RpcPort;
            server.RPCOptions.ParameterFormater = new JsonPacket();
        }, null, assemblies);
        return services;
    }

    public static IServiceCollection AddXRpcClient(this IServiceCollection services, IConfigurationSection section)
    {
        services.Configure<List<RpcClientService>>(section);
        services.AddSingleton<IRpcClient>(p =>
        {
            var options = p.GetService<IOptions<List<RpcClientService>>>();

            var discover = p.GetService<IDiscover>();
            if (discover == null)
                throw new ArgumentNullException("IDiscover Not added to IServiceCollection");

            var clusterService = discover.GetService();
            IDictionary<string, IDictionary<string, string>> serviceDict = new Dictionary<string, IDictionary<string, string>>();
            foreach (var kv in clusterService)
                serviceDict.TryAdd(kv.Key, kv.Value.Meta);

            return new BeetleXRpcClient(options.Value, serviceDict);
        });
        return services;
    }

    #endregion

}